package com.ctw.mall.service.impl;

import com.ctw.mall.dao.OrderItemMapper;
import com.ctw.mall.dao.OrderMapper;
import com.ctw.mall.dao.ProductMapper;
import com.ctw.mall.dao.ShippingMapper;
import com.ctw.mall.enums.OrderStatusEnum;
import com.ctw.mall.enums.PaymentTypeEnum;
import com.ctw.mall.enums.ProductStatusEnum;
import com.ctw.mall.enums.ResponseEnum;
import com.ctw.mall.pojo.*;
import com.ctw.mall.service.ICartService;
import com.ctw.mall.service.IOrderService;
import com.ctw.mall.vo.OrderItemVo;
import com.ctw.mall.vo.OrderVo;
import com.ctw.mall.vo.ResponseVo;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

import static com.ctw.mall.enums.OrderStatusEnum.NO_PAY;


@Service
public class OrderServiceImpl implements IOrderService {

    @Autowired
    private ShippingMapper shippingMapper;

    @Autowired
    private ICartService cartService;

    @Autowired
    private ProductMapper productMapper;

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private OrderItemMapper orderItemMapper;

    @Override
    @Transactional  //回滚条件，出现RuntimeException，数据库写入失败其父类都为RuntimeException，因此回滚
    //该方法内任何一个数据库的操作的失败都会回滚
    //数据库所支持的功能，底层作用是由数据库决定的，并不是java代码的事务
    public ResponseVo<OrderVo> create(Integer uid, Integer shippingId) {
        //收货地址校验（而且需要返回）
        Shipping shipping = shippingMapper.selectByUidAndShippingId(uid, shippingId);
        if(shipping == null){
            return ResponseVo.error(ResponseEnum.SHIPPING_NOT_EXIST);
        }

        //1.获取购物车，校验（是否有商品，库存是否充足）
        //从redis中获取,购物车模块中获取购物车列表,取出选中的商品，得到被选中商品的列表
        List<Cart> cartList = cartService.listForCart(uid).stream()
                .filter(Cart::getProductSelected)
                .collect(Collectors.toList());
        //对于list用isEmpty方法判断,判断是有选中商品
        if(CollectionUtils.isEmpty(cartList)){
            return ResponseVo.error(ResponseEnum.CART_SELECT_IS_EMPTY);
        }
        //数据库中是否有商品,不在for循环当中写sql,优化
        //获取cartList里的products,先查出所有的productId，形成集合set
        Set<Integer> productIdSet = cartList.stream().map(Cart::getProductId)
                .collect(Collectors.toSet());
        //根据ProductIdSet一次性查询商品，形成map对象，
        List<Product> productList = productMapper.selectByProductIdSet(productIdSet);
        Map<Integer,Product> map = productList.stream()
                        .collect(Collectors.toMap(Product::getId,product -> product));


        //生成orderNo
        Long orderNo = generateOrderNo();
        List<OrderItem> orderItemList = new ArrayList<>();
        //从购物车中拿到的Id和productList当中的比较，若在productList没有，则此商品不存在
        //遍历被选中的商品，获取每个商品的id，与id匹配的product对象看是否为空
        for (Cart cart : cartList) {
            Product product = map.get(cart.getProductId());
            if(product == null){
                return ResponseVo.error(ResponseEnum.PRODUCT_NOT_EXIST,
                           "商品不存在.productId ="+cart.getProductId() );
            }

            //商品的上下架状态的判断
            if(!ProductStatusEnum.ON_SALE.getCode().equals(product.getStatus())){
                return ResponseVo.error(ResponseEnum.PRODUCT_OFF_SALE_OR_DELETE,
                                        "商品不是在售状态."+product.getName());
            }

            //判断库存是否充足
            if(product.getStock()<cart.getQuantity()){
                return ResponseVo.error(ResponseEnum.PRODUCT_STOCK_LACK,
                        "库存不正确" + product.getName());
            }

            OrderItem orderItem = buildOrderItem(uid, orderNo, product, cart.getQuantity());
            orderItemList.add(orderItem);
            
            
            //已判断库存是否充足，减库存
            product.setStock(product.getStock() - cart.getQuantity());
            int row = productMapper.updateByPrimaryKeySelective(product);
            if(row<=0){
                return ResponseVo.error(ResponseEnum.ERROR);
            }

        }
        //2.计算总价，选中的商品,buildOrderItem当中
        //3.生成订单，入库，order和order_item,事务
        //构建order对象
        Order order = buildOrder(uid, orderNo, shippingId, orderItemList);

        int rowOrder = orderMapper.insertSelective(order);
        if(rowOrder<=0){
            return ResponseVo.error(ResponseEnum.ERROR);
        }

        int rowOrderItem = orderItemMapper.batchInsert(orderItemList);
        if(rowOrderItem<=0){
            return ResponseVo.error(ResponseEnum.ERROR);
        }

        //4.更新购物车(选中的商品)
        //redis有事务（打包命令）,不能回滚,redis是单线程的，命令都是原子性的
        //不在前面的for里面写，因为不能保证每件商品都能完成上述步骤，若未完成，redis不会回滚，造成错误
        for (Cart cart : cartList) {
            cartService.delete(uid,cart.getProductId());
        }

        //5.构造orderVo
        OrderVo orderVo = buildOrderVo(order, orderItemList, shipping);


        return ResponseVo.success(orderVo);
    }


    //订单列表
    //通过uid查出所有的订单，形成一个列表
    //从此列表中获取其中的orderNo形成一个集合
    //每一个对应的orderNo对应可能有多件商品，但是orderItemList是所有的订单的orderItem并非与每一个orderNo对应
    //用orderNo将orderItemList分开

    @Override
    public ResponseVo<PageInfo> list(Integer uid, Integer pageNum, Integer pageSize) {

        PageHelper.startPage(pageNum,pageSize);
        List<Order> orderList = orderMapper.selectByUid(uid);

        Set<Long> orderNoSet = orderList.stream().map(Order::getOrderNo)
                .collect(Collectors.toSet());
        List<OrderItem> orderItemList = orderItemMapper.selectByOrderNoSet(orderNoSet);

        //map对象一个key，对应多个value即一个list
        Map<Long,List<OrderItem>> orderItemMap=orderItemList.stream()
                .collect(Collectors.groupingBy(OrderItem::getOrderNo));


        Set<Integer> shippingIdSet = orderList.stream().map(Order::getShippingId)
                .collect(Collectors.toSet());

        List<Shipping> shippingList = shippingMapper.selectByIdSet(shippingIdSet);
        Map<Integer,Shipping> shippingMap =shippingList.stream()
                .collect(Collectors.toMap(Shipping::getId,shipping -> shipping));

        List<OrderVo> orderVoList = new ArrayList<>();
        for (Order order : orderList) {
            //此处的orderItemList是与order对应的，而不是从数据库中查出的所有的orderItemList
            //其中一个orderNo对应着多个orderItem
            List<OrderItem> orderItemListEnd=orderItemMap.get(order.getOrderNo());
            if(orderItemListEnd==null){
                return ResponseVo.error(ResponseEnum.NOT_FIND_PRODUCT_IN_ORDER);
            }
            Shipping shipping=shippingMap.get(order.getShippingId());
            if(shipping==null){
                return ResponseVo.error(ResponseEnum.SHIPPING_NOT_EXIST);
            }
            OrderVo orderVo=buildOrderVo(order, orderItemListEnd, shipping);

            orderVoList.add(orderVo);
        }
        PageInfo pageInfo = new PageInfo(orderList);
        pageInfo.setList(orderVoList);
        return ResponseVo.success(pageInfo);
    }


    @Override
    public ResponseVo<OrderVo> detail(Integer uid, Long orderNo) {

        Order order = orderMapper.selectByOrderNo(orderNo);
        //得到订单后判断订单是否存在，是否属于uid这个人
        if(order==null || !order.getUserId().equals(uid)){
            return ResponseVo.error(ResponseEnum.ORDER_NOT_EXIST);
        }

        Set<Long> orderNoSet = new HashSet<>();
        orderNoSet.add(orderNo);
        List<OrderItem> orderItemList = orderItemMapper.selectByOrderNoSet(orderNoSet);

        Shipping shipping = shippingMapper.selectByPrimaryKey(order.getShippingId());

        OrderVo orderVo = buildOrderVo(order, orderItemList, shipping);
        return ResponseVo.success(orderVo);
    }

    //取消订单
    @Override
    public ResponseVo cancel(Integer uid, Long orderNo) {
        Order order = orderMapper.selectByOrderNo(orderNo);
        //得到订单后判断订单是否存在，是否属于uid这个人
        if(order==null || !order.getUserId().equals(uid)){
            return ResponseVo.error(ResponseEnum.ORDER_NOT_EXIST);
        }
        //此处只设置了未付款才能取消
        if(!order.getStatus().equals(NO_PAY.getCode())){
            return ResponseVo.error(ResponseEnum.ORDER_STATUS_ERROR);
        }
        order.setStatus(OrderStatusEnum.CANCELED.getCode());
        order.setCloseTime(new Date());
        int row = orderMapper.updateByPrimaryKeySelective(order);
        if(row<=0){
            return ResponseVo.error(ResponseEnum.ERROR);
        }
        return ResponseVo.success();
    }




    //构建Order_item对象
    private OrderItem buildOrderItem(Integer uid,Long orderNo,Product product,Integer quantity) {
        OrderItem item = new OrderItem();
        item.setUserId(uid);
        item.setOrderNo(orderNo);
        item.setProductId(product.getId());
        item.setProductName(product.getName());
        item.setProductImage(product.getMainImage());
        item.setCurrentUnitPrice(product.getPrice());
        item.setQuantity(quantity);
        item.setTotalPrice(product.getPrice().multiply(BigDecimal.valueOf(quantity)));
        return item;
    }

    /**
     * 企业级:分布式唯一id主键
     * 生成id
     * @return
     */
    private Long generateOrderNo() {
        return System.currentTimeMillis()+new Random().nextInt(999);
    }

    private Order buildOrder(Integer uid,Long orderNo,Integer shippingId,
                             List<OrderItem> orderItemList) {
        BigDecimal payment = orderItemList.stream().map(OrderItem::getTotalPrice)
                .reduce(BigDecimal.ZERO, BigDecimal::add);

        Order order = new Order();
        order.setUserId(uid);
        order.setOrderNo(orderNo);
        order.setShippingId(shippingId);
        order.setPayment(payment);
        order.setPaymentType(PaymentTypeEnum.PAY_ONLINE.getCode());
        //运费，并未涉及直接传值
        order.setPostage(10);
        order.setStatus(NO_PAY.getCode());
        return order ;
    }

    /**
     * 构造返回的OrderVo对象
     */
    private OrderVo buildOrderVo(Order order, List<OrderItem> orderItemList, Shipping shipping) {
        OrderVo orderVo = new OrderVo();
        BeanUtils.copyProperties(order,orderVo);

        List<OrderItemVo> orderItemVoList = orderItemList.stream().map(e -> {
            OrderItemVo orderItemVo = new OrderItemVo();
            BeanUtils.copyProperties(e, orderItemVo);
            return orderItemVo;
        }).collect(Collectors.toList());

        orderVo.setOrderItemVoList(orderItemVoList);
        if(shipping != null) {
            orderVo.setShippingId(shipping.getId());
            orderVo.setShippingVo(shipping);
        }
        return orderVo;
    }

}
